//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package impl

import (
	"context"
	"net/url"
	"os"

	"database/sql"

	"atomgit.com/openlinksaas-org/sql-proxy/gen_code"
	"github.com/go-sql-driver/mysql"
	"github.com/google/uuid"
	_ "github.com/lib/pq"
)

type ConnectionApiImpl struct{}

func (s *ConnectionApiImpl) ConnectionClosePost(ctx context.Context, req gen_code.ConnectionClosePostRequest) (gen_code.ImplResponse, error) {
	if req.Token != token {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: "wrong token",
			},
		}, nil
	}
	infoItem, ok := clientMap.Load(req.Id)
	if !ok {
		return gen_code.ImplResponse{
			Code: 200,
			Body: req.Id,
		}, nil
	}
	info := infoItem.(*ClientInfo)
	info.client.Close()
	clientMap.Delete(req.Id)
	return gen_code.ImplResponse{
		Code: 200,
		Body: req.Id,
	}, nil
}

func (s *ConnectionApiImpl) ConnectionListPost(ctx context.Context, req gen_code.ConnectionListPostRequest) (gen_code.ImplResponse, error) {
	if req.Token != token {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: "wrong token",
			},
		}, nil
	}
	retList := []gen_code.ConnectionListPost200ResponseInner{}
	clientMap.Range(func(key, value any) bool {
		info := value.(*ClientInfo)
		retList = append(retList, gen_code.ConnectionListPost200ResponseInner{
			Id:         key.(string),
			Name:       info.Name,
			Driver:     info.Driver,
			DataSource: info.DataSource,
		})
		return true
	})
	return gen_code.ImplResponse{
		Code: 200,
		Body: retList,
	}, nil
}

func (s *ConnectionApiImpl) ConnectionOpenPost(ctx context.Context, req gen_code.ConnectionOpenPostRequest) (gen_code.ImplResponse, error) {
	if req.Token != token {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: "wrong token",
			},
		}, nil
	}

	support := false
	for index, arg := range os.Args {
		if index > 0 && arg == req.Driver {
			support = true
			break
		}
	}

	if !support {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: "not support driver",
			},
		}, nil
	}

	client, err := sql.Open(req.Driver, req.DataSource)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	dataSource := ""
	if req.Driver == "mysql" {
		cfg, err := mysql.ParseDSN(req.DataSource)
		if err != nil {
			return gen_code.ImplResponse{
				Code: 500,
				Body: gen_code.ErrInfo{
					ErrMsg: err.Error(),
				},
			}, nil
		}
		if cfg.Passwd != "" {
			cfg.Passwd = "******"
		}
		dataSource = cfg.FormatDSN()
	} else if req.Driver == "postgres" {
		u, err := url.Parse(req.DataSource)
		if err != nil {
			return gen_code.ImplResponse{
				Code: 500,
				Body: gen_code.ErrInfo{
					ErrMsg: err.Error(),
				},
			}, nil
		}
		passwd, _ := u.User.Password()
		if passwd != "" {
			u.User = url.UserPassword(u.User.Username(), "******")
		}
		dataSource = u.String()
		rows, err := client.Query("select 1")
		if err != nil {
			return gen_code.ImplResponse{
				Code: 500,
				Body: gen_code.ErrInfo{
					ErrMsg: err.Error(),
				},
			}, nil
		}
		rows.Close()
	}
	id := uuid.NewString()
	clientMap.Store(id, &ClientInfo{
		Name:          req.Name,
		Driver:        req.Driver,
		DataSource:    dataSource,
		rawDataSource: req.DataSource,
		client:        client,
	})
	return gen_code.ImplResponse{
		Code: 200,
		Body: id,
	}, nil
}
